package ar.uba.fi.algo3.algoTank.Objetos;

import java.io.Serializable;
import java.util.LinkedList;

import ar.uba.fi.algo3.algoTank.ObservadorJuego;
import ar.uba.fi.algo3.algoTank.Interfaces.Interactuable;

public class Territorio implements Serializable{
	
	private static final long serialVersionUID = 6L;
	private float ancho;
	private float alto;
	private LinkedList<Objeto> objetos; //todos los elementos que contiene	
	private LinkedList<ObservadorJuego> observadoresJuego;
	
		
	public Territorio(){
		this.ancho = 600;
		this.alto = 400;		
		this.objetos = new LinkedList<Objeto>();	
		this.observadoresJuego = new LinkedList<ObservadorJuego>();	
	}
	
	//PUBLIC
	public float getAncho() {
		return ancho;
	}
	
	public float getAlto() {
		return alto;
	}
			
	public LinkedList<Objeto> getObjetos(){
		return objetos;
	}
	
	//llama a todas las acciones de un ciclo del juego
	public void actualizar() {
		this.actualizarObjetos();
		this.resolverColisiones();
		this.limpiarMuertos();
	}
	
	
	//devuelve true si la ubicacion no esta sobre ninguna pared
	//ni tampoco sale de los limites del mapa
	public boolean ubicacionLibre(Ubicacion ubicacion){		
		
	/*	if ( (!this.impactaPared(ubicacion)) & (!this.fueraDelTerritorio(ubicacion)) )
				return true;
		return false;*/
				
				
	return (!this.impactaPared(ubicacion) & 
				!this.fueraDelTerritorio(ubicacion)); 
	}
	
	
	public boolean fueraDelTerritorio(Ubicacion ubicacion) {
		
		if ((ubicacion.maxPosAderecha() > this.ancho) || (ubicacion.minPosAizquierda() < 0))
			return true;
				
		if ((ubicacion.maxPosArriba() > this.alto) || (ubicacion.minPosAbajo() < 0))
			return true;
		
		return false;
	}
	
	
	public synchronized void addObjeto(Objeto obj) {
		objetos.add(obj);
		avisarNuevoObjeto(obj);
	}
	
	public void avisarNuevoObjeto(Objeto obj){
		for(ObservadorJuego obs : observadoresJuego)
			obj.avisarNuevoObjeto(obs);
	}
	
	public synchronized void deleteObjeto(Objeto obj){
		objetos.remove(obj);
		avisarObjetoMuerto(obj);
	}
	
	public void deleteAllObjetos(){
		
	LinkedList<Objeto> listaObjetos = this.clonarObjetos();
		
		for(Objeto obj : listaObjetos)
			 this.deleteObjeto(obj);
		
	}
	
	public void avisarObjetoMuerto(Objeto obj){
		for(ObservadorJuego obs : observadoresJuego)
			obs.nuevoObjetoMuerto(obj);
	}

	
	
	//actualiza en el tiempo todos los objetos
	private void actualizarObjetos() {
		LinkedList<Objeto> listaObjetos = this.clonarObjetos();
		
		for(Objeto obj : listaObjetos)
				obj.vivir();
	}
	
	
	//ordena interactuar a todos los objetos que estan colisionando
	public void resolverColisiones() {
		LinkedList<Interactuable> interactuables = this.obtenerInteractuables();
		LinkedList<Objeto> listaObjetos = this.clonarObjetos();
					
		for(Interactuable obj : interactuables) 
			for(Objeto obj2 : listaObjetos) 
				    if (obj.interactua(obj2))
				 	     obj.interactuar(obj2);
	}
		
	
	//retorna una lista con todos los objetos que resuelven colisiones
	public LinkedList<Interactuable> obtenerInteractuables(){
		LinkedList<Interactuable> lista = new LinkedList<Interactuable>();
		
		for(Objeto obj: objetos)
			if (obj.esInteractuable())
				lista.add((Interactuable) obj);

		return lista;		
	}
	
	
	public LinkedList<Objeto> obtenerParedes() {
		LinkedList<Objeto> lista = new LinkedList<Objeto>();
		
		for(Objeto obj: objetos)
			if (obj.esTraspasable())
				lista.add(obj);
		
		return lista;
	}
	
	
	//retira del territorio todos los objetos que murieron
	private void limpiarMuertos(){
		LinkedList<Objeto> listaObjetos = this.clonarObjetos();

		for(Objeto obj : listaObjetos)
			if(!obj.estaVivo())
				this.deleteObjeto(obj);
	}
		
	
	//devuelve una copia de la lista de objetos
	private LinkedList<Objeto> clonarObjetos() {
		LinkedList<Objeto> lista = new LinkedList<Objeto>();
		
		for(Objeto obj : objetos)
				lista.add(obj);
		
		return lista;
	}
	
	
	public boolean impactaPared(Ubicacion ubicacion) {
		LinkedList<Objeto> lista = this.obtenerParedes();
		for (Objeto bloque : lista)
			if (bloque.getUbicacion().comparteUbicacion(ubicacion))
				return true;
		return false;	
	}

	
	public void addObservadorJuego(ObservadorJuego obs) {
		this.observadoresJuego.add(obs);		
	}
	
}
